#include <time.h>

#include "lib.h"
#include "endian.h"
#include "filestream.h"
#include "hashtable.h"
#include "rfilewriter.h"
#include "rentrywriter.h"
#include "rresource.h"
using namespace Library;

#define SUPER	ResourceFile
#define SELF	ResourceFileWriter


//----------------------------------------------------------------------------
// Public
//
ResourceFileWriter::ResourceFileWriter()
	: SUPER()
{
	reset();
}


ResourceFileWriter::~ResourceFileWriter()
{
}


bool8
ResourceFileWriter::open(const String & filename)
{
	return (bool8)(reset() && SUPER::open(filename));
}


bool8
ResourceFileWriter::close()
{
	return (bool8)(reset() && SUPER::close());
}


bool8
ResourceFileWriter::add(const String & name, uint32 size, uint8 * data)
{
	pResourceEntry	entry;

	// Duplicate resources are not allowed.
	if (m_entries->get(name, &entry))
		return false;

	// Create and add to the hashtable.
	entry = new ResourceEntryWriter(name, size, data);
	if (entry)
		return m_entries->add(name, entry);

	return false;
}


bool8
ResourceFileWriter::compression()
{
	return m_compression;
}


void
ResourceFileWriter::compression(bool8 compression)
{
	m_compression = compression;
}


ResourceFile::Format
ResourceFileWriter::format()
{
	ResourceFile::Format	format;

	format.m_dif_entry		= m_header.m_dif_entry;
	format.m_dif_index		= m_header.m_dif_index;
	format.m_dif_header		= m_header.m_dif_header;
	format.m_compression	= m_compression;

	return format;
}


bool8
ResourceFileWriter::format(Format format)
{
	if (format.isValid())
	{
		m_header.m_dif_entry	= format.m_dif_entry;
		m_header.m_dif_index	= format.m_dif_index;
		m_header.m_dif_header	= format.m_dif_header;
		m_compression			= format.m_compression;

		return true;
	}
	
	return false;
}


bool8
ResourceFileWriter::remove(const String & name)
{
	pResourceEntry	entry;

	if (m_entries->get(name, &entry))
		return m_entries->remove(name);

	// Removing an already non-existent resource is not an error.
	return true;
}


bool8
ResourceFileWriter::rename(const String & from, const String & to)
{
	pResourceEntry	entry;

	if (m_entries->get(from, &entry))
		return ((pResourceEntryWriter)entry)->name(to);

	// Renaming a non-existent resource is an error.
	return false;
}


bool8
ResourceFileWriter::save(const String & filename)
{
	FileStream	stream;

	if (stream.open(filename, FileStream::writing))
		return save(&stream);

	return false;
}


bool8
ResourceFileWriter::save(pStream stream)
{
	HashTableIterator<pResourceEntry>		iterator(m_entries);
	pResourceEntryWriter					entry;
	Index									index;

	// The current selected format must be valid.
	if (format().isValid() == false)
		return false;

	// Populate the index header (m_offset will be corrected later).
	memset(&index, 0, sizeof index);
	index.m_offset	= 0;
	index.m_entries	= m_entries->size();

	// Write out the file header.
	if (stream->write(&m_header, sizeof m_header) != sizeof m_header)
		return false;

	// Write out all the resource data.
	for (iterator.begin(); iterator.key(); iterator.operator++(0))
	{
		entry = (pResourceEntryWriter)iterator.value();
		if (!entry->saveData(stream, m_compression))
			return false;
	}

	// Now we correct index.m_offset.
	index.m_offset = stream->tell();
	if (index.m_offset == Stream::eof)
		return false;

	// Write out all the resource entries.
	for (iterator.begin(); iterator.key(); iterator.operator++(0))
	{
		entry = (pResourceEntryWriter)iterator.value();
		if (!entry->save(m_header.m_dif_entry))
			return false;
	}

	// Now we correct m_header.m_offset.
	m_header.m_offset = stream->tell();
	if (m_header.m_offset == Stream::eof)
		return false;

	// Write out the index header.
	if (stream->write(&index, sizeof index) != sizeof index)
		return false;

	// Write out the file header.
	if (stream->seek(0, Stream::beginning) != 0 ||
		stream->write(&m_header, sizeof m_header) != sizeof m_header)
		return false;

	return true;
}


String
ResourceFileWriter::tocHeader()
{
	String	header;

	header.sprintf("\nResource File - Table of contents"
				   "\n  version    : %d.%d.%d.%d-%c%c%c (%s)"
				   "\n  compressed : %s"
				   "\n",
				   m_header.m_version[0],
				   m_header.m_version[1],
				   m_header.m_version[2],
				   m_header.m_version[3],
				   m_header.m_dif_header ? m_header.m_dif_header : 'A',
				   m_header.m_dif_index  ? m_header.m_dif_index  : 'A',
				   m_header.m_dif_entry  ? m_header.m_dif_entry  : 'A',
				   m_header.m_endian == 'e' ? "little-endian" :
				   m_header.m_endian == 'E' ? "big-endian" : "unknown",
				   compression() ? "yes" : "no");

	header += ResourceEntryWriter::tocHeader();

	return header;
}


String
ResourceFileWriter::tocDetailsOf(const String & name)
{
	pResourceEntry	entry;
	
	if (m_entries->get(name, &entry))
		return ((pResourceEntryWriter)entry)->tocDetails();

	return "";
}


//----------------------------------------------------------------------------
// Protected
//
bool8
ResourceFileWriter::archive(Header * header)
{
	// Default values.
	memset(&m_header, 0, sizeof Header);
	memcpy(m_header.m_magic, RF_MAGIC, strlen(RF_MAGIC));
	m_header.m_version[0]	= RF_VERSION_MAJOR;
	m_header.m_version[1]	= RF_VERSION_MINOR;
	m_header.m_version[2]	= RF_VERSION_PATCH;
	m_header.m_version[3]	= RF_VERSION_BUILD;
	m_header.m_endian		= endianLITTLE_ENDIAN;
	m_header.m_dif_entry	= RE_CURRENT_DISKIMAGEFORMAT;
	m_header.m_dif_index	= RI_CURRENT_DISKIMAGEFORMAT;
	m_header.m_dif_header	= RH_CURRENT_DISKIMAGEFORMAT;
	m_header.m_offset		= 0;
	m_header.m_indices		= 1;

	// If a header has been provided, preserve the version and resource format.
	if (header)
	{
		m_header.m_version[0]	= header->m_version[0];
		m_header.m_version[1]	= header->m_version[1];
		m_header.m_version[2]	= header->m_version[2];
		m_header.m_version[3]	= header->m_version[3];
		m_header.m_endian		= header->m_endian;
		m_header.m_dif_entry	= header->m_dif_entry;
		m_header.m_dif_index	= header->m_dif_index;
		m_header.m_dif_header	= header->m_dif_header;

//		if (format() == Format::RESOURCE1)
//			m_compression = false;
	}

	return true;
}


pResourceEntry
ResourceFileWriter::createResourceEntry()
{
	return new ResourceEntryWriter();
}


bool8
ResourceFileWriter::reset()
{
	m_compression = true;

	// Initializes m_header with default values.
	archive(0);

	return true;
}


//
// vim: nowrap
//
